<?php

namespace Yolo\Service\Apis;

use Yolo\Components\EventDispatcher\EventDispatcherComponent;
use Yolo\Di\DI;
use Yolo\Errors\EventNotRegisteredException;
use Yolo\Logs\Log;
use Yolo\Rpc\Annotations\Api;
use Yolo\Rpc\Annotations\RpcController;
use Yolo\Rpc\Rpc;
use Yolo\Service\ApplicationContainer;
use Yolo\Service\RpcApiManager;
use Yolo\Service\RpcRequest;

/**
 * The service api
 * @author LUSHAOMING
 * @date 2024-12-11
 */
#[RpcController]
class ServiceApiController
{
    /**
     * ServiceApiController constructor.
     * @param ApplicationContainer $container
     * @param RpcApiManager $rpcApiManager
     */
    public function __construct(
        private readonly ApplicationContainer $container,
        private readonly RpcApiManager $rpcApiManager,
        private readonly EventDispatcherComponent $eventDispatcherComponent,
        private readonly Log $log,
    ){}

    #[Api([
        'service' => '_',
    ])]
    public function getEndpoints(): array
    {
        return array_values(array_map(function ($api) {
            unset($api['invoke'], $api['options']['preProcessor']);
            return $api;
        }, $this->rpcApiManager->getEndpoints()));
    }

    #[Api([
        'service' => '_',
    ])]
    public function ping(): array
    {
        return Rpc::ok();
    }

    #[Api([
        'service' => '_',
    ])]
    public function getBasicInfo(): array
    {
        $config = $this->container->getConfig();
        $listen =$config->get('rpc.server.listen');

        return [
            'service' => [
                'listen' => [
                    'protocol' => 'http',
                    'host' => $listen['host'] ?? '0.0.0.0',
                    'port' => $listen['port'] ?? 80,
                ],
                'instance' => $config->get('rpc.server.instance') ?? [],
            ],
            'applicationRegistry' => [
                'enabled' => $config->get('applicationRegistry.enabled') ?? false,
            ],
            'system' => [
                'os' => PHP_OS,
            ],
            'php' => [
                'version' => PHP_VERSION,
                'memory' => memory_get_usage(),
            ],
            'swoole' => [
                'version' => SWOOLE_VERSION,
            ]
        ];
    }

    #[Api([
        'service' => '_',
    ])]
    public function getBuildInfo(): array
    {
        return $this->getBasicInfo();
    }
    #[Api([
        'service' => '_',
    ])]
    public function getTunnelApiList(): array
    {
        return $this->getEndpointList('Tunnel');
    }

    #[Api([
        'service' => '_',
    ])]
    public function getRestfulApiList(): array
    {
        return $this->getEndpointList('Restful');
    }

    #[Api([
        'service' => '_',
    ])]
    public function getInternalApiList(): array
    {
        return $this->getEndpointList('Api');
    }

    private function getEndpointList(string $type): array
    {
        $list = array_filter($this->rpcApiManager->getEndpoints(), function ($api) use($type) {

            return $api['type'] === $type;
        });

        return array_values(array_map(function ($api) {
            unset($api['invoke'], $api['options']['preProcessor']);
            return $api;
        }, $list));
    }

    #[Api([
        'service' => '_',
        'validation' => [
            'type' => 'required|string',
            'topic' => 'required|string',
            'data' => 'required|array',
        ],
    ])]
    public function MQDispatchInstance(RpcRequest $request): array
    {
        $body = $request->getBody();

        $events = $this->eventDispatcherComponent->getEvents();
        if (!isset($events[$body['type']][$body['topic']])) {

            $this->log->error([
                'action' => 'mq',
                'message' => 'Error while processing message',
                'data' => [
                    'type' => $body['type'],
                    'topic' => $body['topic'],
                ]
            ]);

            throw new EventNotRegisteredException();
        }

        $call = $events[$body['type']][$body['topic']]['call'];
        $method = $call[1];
        $obj = DI::use($call[0]);
        $obj->$method($body['data']);

        return Rpc::ok();
    }

    #[Api([
        'service' => '_',
        'validation' => [
            'type' => 'required|string',
            'topic' => 'required|string',
            'data' => 'required|array',
        ],
    ])]
    public function MQDispatchService(RpcRequest $request): array
    {
        return $this->MQDispatchInstance($request);
    }
}
